using System;
using System.Configuration;
using System.Security.Cryptography;

namespace nToolbox.Security
{
    /// <summary>
    ///     Provides persistence for application settings classes.  Derives from <c>LocalFileSettingsProvider</c>, which persists the settings to an
    ///     <c>app.config</c> or <c>user.config</c> file, except the setting values are encrypted and decrypted when setting and getting them respectively.
    /// </summary>
    /// <remarks>
    /// Base classes should set the key and iv to use for encryption/decryption, and may optionally change the Name of the Provider.  To use, set the created
    /// base class as the Provider (SettingProviderAttribute) of a setting property.
    /// </remarks>
    /// <seealso cref="LocalFileSettingsProvider"/>
    /// <seealso cref="System.Configuration.Provider.ProviderBase"/>
    public abstract class EncryptedLocalFileSettingsProviderBase<T> : LocalFileSettingsProvider where T : SymmetricAlgorithm, new()
    {
        private readonly SymmetricCryptography<T> crypto;
        private readonly string name = "EncryptedLocalFileSettingsProvider";

        /// <summary>
        /// Constructor with byte array key and iv.
        /// </summary>
        protected EncryptedLocalFileSettingsProviderBase( byte[] key, byte[] iv )
        {
            crypto = new SymmetricCryptography<T>( key, iv );
        }

        /// <summary>
        /// Constructor with string key and iv.
        /// </summary>
        protected EncryptedLocalFileSettingsProviderBase( string key, string iv )
        {
            crypto = new SymmetricCryptography<T>( key, iv );
        }

        /// <summary>
        /// Constructor with byte array key and iv plus provider name.
        /// </summary>
        protected EncryptedLocalFileSettingsProviderBase( byte[] key, byte[] iv, string name )
            : this( key, iv )
        {
            this.name = name;
        }

        /// <summary>
        /// Constructor with string key and iv plus provider name.
        /// </summary>
        protected EncryptedLocalFileSettingsProviderBase( string key, string iv, string name )
            : this( key, iv )
        {
            this.name = name;
        }

        /// <summary>
        ///     <see cref="System.Configuration.Provider.ProviderBase.Name"/>
        /// </summary>
        public override string Name
        {
            get { return name; }
        }

        /// <summary>
        ///     <see cref="LocalFileSettingsProvider.GetPropertyValues"/>
        /// </summary>
        public override SettingsPropertyValueCollection GetPropertyValues( SettingsContext context, SettingsPropertyCollection properties )
        {
            SettingsPropertyValueCollection result = base.GetPropertyValues( context, properties );

            foreach ( SettingsPropertyValue propertyValue in result )
            {
                propertyValue.PropertyValue = crypto.Decrypt( propertyValue.PropertyValue.ToString() );
            }

            return result;
        }

        /// <summary>
        ///     <see cref="LocalFileSettingsProvider.SetPropertyValues"/>
        /// </summary>
        public override void SetPropertyValues( SettingsContext context, SettingsPropertyValueCollection values )
        {
            foreach ( SettingsPropertyValue propertyValue in values )
            {
                propertyValue.PropertyValue = crypto.Encrypt( propertyValue.PropertyValue.ToString() );
            }

            base.SetPropertyValues( context, values );
        }
    }
}
